package com.bluedot.www.core.utils;


import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import com.bluedot.www.core.common.OSSConfigure;
import com.bluedot.www.framework.mvc.servlet.http.MultipartFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;

/**
 * 阿里云OSS 文件管理工具类
 * 注意：每次使用 ossClient后需要调用shutdown()方法，否则会浪费资源
 * 一个项目中如果可以同时存在多个 ossClient，可以并发运行，如果业务规则不允许则应该利用单例模式保证ossClient只有一个对象
 * @author jiangnan
 * @version 1.0
 * @date 2021/8/16 20:21
 */
public class OSSManageUtil {
    /**OSS 配置*/
    public static OSSConfigure ossConfigure;
    /**生命周期*/
    public static SetBucketLifecycleRequest req = null;
//    public static OSS ossClient;

    /**日志，用于记录特定系统或应用的消息*/
    private static final Logger logger = LoggerFactory.getLogger(OSSConfigure.class);

    static {
        ossConfigure = OSSConfigure.getInstance();

//        // 创建OSSClient实例。
//        ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(), ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());
//
//        req = new SetBucketLifecycleRequest(ossConfigure.getBucketName());
//        // 添加Lifecycle规则,默认保存时间为3天
//        req.AddLifecycleRule(new LifecycleRule("delete log files", "log/", LifecycleRule.RuleStatus.Enabled, 3));

    }

    public static String getAccessUrl() {
        return ossConfigure.getAccessUrl();
    }

    /**
     * 上传文件到阿里云oss，默认保留原始文件名
     * <strong>注意：必须要保证文件名（文件路径）是唯一的否则会导致覆盖文件,如果发现重复自动重命名</strong>
     * @param in   文件的输入流
     * @param name 文件名
     * @param path 文件保存的文件夹
     * @return java.lang.String   文件链接
     * @author jiangnan
     * @date 2021/9/1 11:08
     */
    public static String uploadFile(InputStream in, String name, String path) {
        // 填写Bucket名称和Object完整路径。Object完整路径中不能包含Bucket名称。
        String url = path + name;
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(),
                ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());
        boolean found = ossClient.doesObjectExist(OSSConfigure.getInstance().getBucketName(), url);
        int count = 1;
        while (found) {
            // 重命名文件直至不重复
            found = ossClient.doesObjectExist(OSSConfigure.getInstance().getBucketName(), url + "(" + count + ")");
        }
        ossClient.shutdown();
        return upload(in, name, path, true);
    }


    /**
     * 上传文件至阿里云,可以选择是否保留文件名，下载时则使用 filename 作为文件名，
     * 但是保留在阿里云OSS时可以选择保留原文件名还是随机码（即链接是否显示随机码）
     * @param in: 文件输入流
     * @param filename: 文件名
     * @param path: 保存路径
     * @param isKeepFilename: 是否保留原文件名
     * @return java.lang.String
     * @author He Peng
     * @date 2021/10/19 17:31
     */
    public static String upload(InputStream in, String filename, String path, boolean isKeepFilename) {
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(),
                ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());
        //重命名上传的文件
        LocalDate now = LocalDate.now();
        String url = path + now.getYear()+ "/" + now.getMonthValue() + "/" +
                now.getDayOfMonth() + "/";
        if (isKeepFilename) {
            url = url + filename;
        } else {
            url = url + UUID.randomUUID().toString().replaceAll("-", "").substring(16) +
                    filename.substring(filename.lastIndexOf("."));
        }
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentEncoding("utf-8");
        objectMetadata.setCacheControl("no-cache");
        objectMetadata.setHeader("Pragma", "no-cache");
        // 设置请求头，OSS根据请求头中的文件名设置根据链接下载时的文件名
        objectMetadata.setContentDisposition("inline;filename=" + filename);

        // 上传文件
        ossClient.putObject(ossConfigure.getBucketName(), url, in, objectMetadata);
        // 关闭OSSClient
        ossClient.shutdown();
        // 关闭io流
        try {
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 关闭OSSClient
        ossClient.shutdown();
        //返回文件url相对路径
        return url;
    }


    /**
     * 根据文件名删除单个文件
     *
     * @param path 文件链接
     * @return java.lang.String
     * @author jiangnan
     * @date 2021/9/1 11:08
     */

    public static boolean delete(String path) {
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(), ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());
        boolean flag = false;

        //判断当前文件的url是否存在
        boolean exist = ossClient.doesObjectExist(ossConfigure.getBucketName(), path);

        if (exist) {
            flag = true;
            ossClient.deleteObject(ossConfigure.getBucketName(), path);
        } else {
            logger.info("The file does not exist. File path: " + path);
        }
        ossClient.shutdown();
        return flag;

    }


    /**
     * 设置定时删除日志文件的时间
     *
     * @param time 时间
     * @return boolean
     * @author jiangnan
     * @date 2021/9/1 14:28
     */
    public static void setAutoTime(Integer time) {
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(), ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());
        //删除之前的生命周期规则
        ossClient.deleteBucketLifecycle(ossConfigure.getBucketName());
        //设置新的生命周期规则
        // 添加Lifecycle规则,默认保存时间为3天
        req.AddLifecycleRule(new LifecycleRule("delete log files", "log/", LifecycleRule.RuleStatus.Enabled, time));
        ossClient.shutdown();
    }


    /**
     * 根据链接获取图片文件
     *
     * @param path 文件相对路径
     * @return java.awt.image.BufferedImage
     * @author jiangnan
     * @date 2021/9/1 17:32
     */
    public static BufferedImage transfor(String path) {
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(), ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());
        String link = ossConfigure.getAccessUrl() + path;
        URL url = null;
        BufferedImage image = null;
        try {
            url = new URL(link);
            image = ImageIO.read(url);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 判断当前文件的url是否存在
        if (image == null) {
            logger.info("The file does not exist. File path: " + path);
        }
        return image;
    }

    /**
     * 根据相对路径从远程批量获取文件
     * @param pathList: 文件的相对路径集合
     * @return java.util.List<java.io.InputStream>
     * @author He Peng
     * @date 2021/10/22 16:53
     */
    public static List<InputStream> getFileFromUrlBatch(List<String> pathList) {
        List<InputStream> imageList = new ArrayList<>();
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(),
                ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());
        OSSObject object = null;

        try {
            for (String path : pathList) {
                object = ossClient.getObject(ossConfigure.getBucketName(), path);
                InputStream in = intransforMyIn(object.getObjectContent());
                imageList.add(in);
            }
            if (object != null) {
                object.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            ossClient.shutdown();
        }
        return imageList;
    }


    /**
     * 根据相对地址获取阿里云的文件，并且获取文件名
     * @param pathList 相对地址集合
     * @return 文件名与对应输入流的哈希表
     */
    public static Map<String, InputStream> getFileInfoFromUrlBatch(List<String> pathList) {
        Map<String, InputStream> inputStreamMap = new HashMap<>(16);
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(),
                ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());
        OSSObject object = null;

        try {
            for (String path : pathList) {
                object = ossClient.getObject(ossConfigure.getBucketName(), path);
                String header = object.getObjectMetadata().getContentDisposition();
                String filename = header.substring(header.indexOf("filename=") + 9);
                InputStream in = intransforMyIn(object.getObjectContent());
                inputStreamMap.put(filename, in);
            }
            assert object != null;
            object.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            ossClient.shutdown();
        }
        return inputStreamMap;
    }

    /**
     * 按照path路径将其文件夹下的所有子文件删除
     *
     * @param path 文件夹路径
     */
    public static void deleteAllByPath(String path) {
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(), ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());
        String marker = null;
        ObjectListing listing;
        do {
            // 构造ListObjectsRequest请求
            ListObjectsRequest listObjectsRequest = new ListObjectsRequest(ossConfigure.getBucketName(), path, marker, null, 1000);
            listing = ossClient.listObjects(listObjectsRequest);
            List<OSSObjectSummary> objectSummaries = listing.getObjectSummaries();
            // 遍历所有Object:目录下的文件
            for (OSSObjectSummary objectSummary : objectSummaries) {
                //key：fun/like/001.avi等，即：Bucket中存储文件的路径
                String key = objectSummary.getKey();
                if (key.equals(path)){
                    continue;
                }
                //key：fun/like/001.avi等，即：Bucket中存储文件的路径
                ossClient.deleteObject(ossConfigure.getBucketName(), key);
            }
            marker = listing.getNextMarker();
        } while (listing.isTruncated());
        System.out.println("删除完成");
        // 关闭client
        ossClient.shutdown();
    }

    /**
     * 根据相对路径获取文件输入流
     *
     * @param path 文件相对路径
     * @return InputStream
     */
    public static InputStream getFileInputStream(String path) {
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(), ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());
        OSSObject object = ossClient.getObject(ossConfigure.getBucketName(), path);
        InputStream in = null;
        try {
            in = intransforMyIn(object.getObjectContent());
            object.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭client
            ossClient.shutdown();
        }

        return in;
    }

    /**
     * 按照一个文件夹路径，把所有流文件输入其中
     *
     * @param ins       输入流
     * @param fileTypes 文件类型（如：.jpg）
     * @param path      保存阿里云中哪个文件夹
     * @throws IOException 异常
     */
    public static List<String> uploadFiles(List<InputStream> ins, List<String> fileTypes, String path) throws IOException {
        if (ins.size() != fileTypes.size()) {
            return null;
        }
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(), ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());


        //重命名上传的文件
        ArrayList<String> list = new ArrayList<>();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
        for (int i = 0; i < ins.size(); i++) {
            String fileName = path + "/" + formatter.format(new Date()) + "/" +
                    UUID.randomUUID().toString().replace("-", "").substring(16) + fileTypes.get(i);
            fileName = fileName.replaceAll("//", "/");
            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentLength(ins.get(i).available());
            objectMetadata.setContentEncoding("utf-8");
            objectMetadata.setCacheControl("no-cache");
            objectMetadata.setHeader("Pragma", "no-cache");
            objectMetadata.setContentDisposition("inline;filename=" + fileName);

            // 上传文件
            ossClient.putObject(ossConfigure.getBucketName(), fileName, ins.get(i), objectMetadata);
            list.add(fileName);
            // 关闭io流
            ins.get(i).close();
        }
        // 关闭OSSClient
        ossClient.shutdown();
        //返回文件url
        return list;
    }


    public static List<String> uploadFiles(List<MultipartFile> fileList, String path) {
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(), ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());

        //重命名上传的文件
        ArrayList<String> list = new ArrayList<>();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
        try {
            for (MultipartFile multipartFile : fileList) {
                String originalFilename = multipartFile.getOriginalFilename();
                if (multipartFile.getOriginalFilename().contains(".")) {
                    originalFilename = multipartFile.getOriginalFilename();
                } else if(multipartFile.getOriginalFilename().contains("blob")) {
                    originalFilename = multipartFile.getName();
                }
                // 生成保存在远程的文件名和保存路径
                String fileName = path + "/" + formatter.format(new Date()) + "/";
                fileName += UUID.randomUUID().toString().replace("-", "").substring(16) +
                        originalFilename.substring(originalFilename.lastIndexOf("."));
                fileName = fileName.replaceAll("//", "/");
                ObjectMetadata objectMetadata = new ObjectMetadata();
                objectMetadata.setContentLength(multipartFile.getInputStream().available());
                objectMetadata.setContentEncoding("utf-8");
                objectMetadata.setCacheControl("no-cache");
                objectMetadata.setHeader("Pragma", "no-cache");
                objectMetadata.setContentDisposition("inline;filename=" + fileName);
                // 上传文件
                ossClient.putObject(ossConfigure.getBucketName(), fileName,
                        multipartFile.getInputStream(), objectMetadata);
                list.add(fileName);
                // 关闭io流
                multipartFile.getInputStream().close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭OSSClient
            ossClient.shutdown();
        }
        //返回文件url
        return list;
    }


    /**
     * 按照map中string类型的url将输入流上传至OSS
     *
     * @param routeIns url：输入流
     * @return
     */
    public static List<String> uploadAllFiles(Map<String, InputStream> routeIns) throws IOException {
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(), ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());

        //重命名上传的文件
        ArrayList<String> list = new ArrayList<>();
//        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        int i = 0;
        for (Map.Entry<String, InputStream> ins : routeIns.entrySet()) {
            //此处直接用ins.getKey()得到路径即可
//            String fileName = ins.getKey().substring(0, ins.getKey().lastIndexOf("/")) + "/" + formatter.format(new Date()) + "_" + UUID.randomUUID() + ins.getKey().substring(ins.getKey().lastIndexOf(".")) ;
            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentEncoding("utf-8");
            objectMetadata.setCacheControl("no-cache");
            objectMetadata.setHeader("Pragma", "no-cache");
            objectMetadata.setContentDisposition("inline;filename=" + ins.getKey());

            // 上传文件
            ossClient.putObject(ossConfigure.getBucketName(), ins.getKey(), ins.getValue(), objectMetadata);
            list.add(ins.getKey());
        }
        // 关闭OSSClient
        ossClient.shutdown();
        //返回文件ur
        return list;
    }

    /**
     * 将一个文件路径下的所有文件以url：输入流形式返回
     *
     * @param path 文件路径
     * @return
     */
    public static Map<String, InputStream> downloadAllByPath(String path) throws IOException {
        Map<String, InputStream> routeIns = new HashMap<>();
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(), ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());

        String marker = null;
        ObjectListing listing = null;
        do {
            // 构造ListObjectsRequest请求
            ListObjectsRequest listObjectsRequest = new ListObjectsRequest(ossConfigure.getBucketName(), path, marker, null, 1000);
            listing = ossClient.listObjects(listObjectsRequest);
            List<OSSObjectSummary> objectSummaries = listing.getObjectSummaries();
            // 遍历所有Object:目录下的文件
            for (OSSObjectSummary objectSummary : objectSummaries) {
                //key：fun/like/001.avi等，即：Bucket中存储文件的路径
                String key = objectSummary.getKey();
                if (key.equals(path)){
                    continue;
                }
                OSSObject object = ossClient.getObject(ossConfigure.getBucketName(), key);
                //放入绝对路径、输出流
                routeIns.put(key, intransforMyIn(object.getObjectContent()));
                object.close();
            }
            marker = listing.getNextMarker();
        } while (listing.isTruncated());

        System.out.println("下载完成");
        // 关闭client
        ossClient.shutdown();
        return routeIns;
    }

    /**
     * 字符输入流转为字符输入流，进行字符输入流的复制操作
     *
     * @param in
     * @return java.io.InputStream
     */
    public static InputStream intransforMyIn(InputStream in) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = in.read(buffer)) > -1) {
            baos.write(buffer, 0, len);
        }
        baos.flush();
        InputStream myIn = new ByteArrayInputStream(baos.toByteArray());
        baos.close();
        return myIn;
    }

    /**
     * 文件拷贝  备份
     *
     * @param
     * @return void
     * @author jiangnan
     * @date 2021/9/8 19:43
     */
    public static void copy() {
        OSS ossClient = new OSSClientBuilder().build(ossConfigure.getEndpoint(),
                ossConfigure.getAccessKeyId(), ossConfigure.getAccessKeySecret());
        // 填写源Object的完整路径。Object完整路径中不能包含Bucket名称。
        String sourceKey = "bluedot";
        // 填写目标Object的完整路径。Object完整路径中不能包含Bucket名称。
        String destinationKey = "backup";
        // 填写源Bucket名称。
        String sourceBucketName = "bluedot-a";
        // 填写与源Bucket处于同一地域的目标Bucket名称。
        String destinationBucketName = "bluedot3";

        CopyObjectResult result = ossClient.copyObject(sourceBucketName, sourceKey, destinationBucketName, destinationKey);
        ossClient.shutdown();

    }


}
